﻿using System;
using System.Collections.Generic;
using LuaInterface;
using UnityEngine;

namespace Framework
{
    public static class GameObjectHelper
    {
        /// <summary>
        /// c#里面GameObject可以直接使用 == 符号，这里主要是为了给lua传递过来的对象做判空处理
        /// </summary>
        /// <param name="go"></param>
        /// <returns></returns>
        private static bool IsNull(GameObject go)
        {
            return go == null || go.Equals(null);
        }

        public static GameObject FindChild(GameObject parentGO, string childPath)
        {
            if (IsNull(parentGO))
            {
                return null;
            }
            var transform = parentGO.transform.Find(childPath);
            return transform ? transform.gameObject : null;
        }

        public static GameObject[] GetRootGameObjects()
        {
            return UnityEngine.SceneManagement.SceneManager.GetActiveScene().GetRootGameObjects();
        }

        public static GameObject Create(GameObject parentGO, string name, bool is2d)
        {
            var go = string.IsNullOrEmpty(name) ? new GameObject() : new GameObject(name);
            if (is2d)
            {
                go.AddComponent<RectTransform>();
            }
            if (!IsNull(parentGO))
            {
                go.transform.SetParent(parentGO.transform, false);
            }
            return go;
        }

        public static Component FindChildComponent(this GameObject parentGO, string childPath, Type componentType)
        {
            var childGO = FindChild(parentGO, childPath);
            if (childGO != null)
            {
                return childGO.GetComponent(componentType);
            }
            return null;
        }

        public static T FindChildComponent<T>(this GameObject parentGO, string childPath) where T : Component
        {
            var childGO = FindChild(parentGO, childPath);
            if (childGO != null)
            {
                return childGO.GetComponent<T>();
            }
            return null;
        }

        public static Component OnceAddComponent(this GameObject go, Type componentType)
        {
            if (!IsNull(go))
            {
                var component = go.GetComponent(componentType);
                if (component == null)
                {
                    component = go.AddComponent(componentType);
                }
                return component;
            }
            return null;
        }

        public static Component OnceAddComponent(this Component comp, Type componentType)
        {
            if (comp != null)
            {
                return OnceAddComponent(comp.gameObject, componentType);
            }
            return null;
        }

        public static T OnceAddComponent<T>(this GameObject gameObject) where T : Component
        {
            if (IsNull(gameObject)) return null;

            T com = gameObject.GetComponent<T>();
            if (com == null)
            {
                com = gameObject.AddComponent(typeof(T)) as T;
            }
            return com;
        }

        public static void SetActive(GameObject go, bool isActive)
        {
            if (!IsNull(go))
            {
                if (go.activeSelf != isActive)
                {
                    go.SetActive(isActive);
                }
            }
        }

        public static void SetActive(Component comp, bool isActive)
        {
            if (comp != null)
            {
                SetActive(comp.gameObject, isActive);
            }
        }

#if UNITY_EDITOR
        private static Dictionary<GameObject, string> goToPath = new Dictionary<GameObject, string>();
        private static Dictionary<string, GameObject> prefabDict = new Dictionary<string, GameObject>();
        private static Texture2D prefabIcon;

        static GameObjectHelper()
        {
            UnityEditor.Editor.finishedDefaultHeaderGUI -= OnDrawHeader;
            UnityEditor.Editor.finishedDefaultHeaderGUI += OnDrawHeader;
            UnityEditor.EditorApplication.hierarchyWindowItemOnGUI -= OnInstanceGUI;
            UnityEditor.EditorApplication.hierarchyWindowItemOnGUI += OnInstanceGUI;
            UnityEditor.EditorApplication.playModeStateChanged -= OnPlayModeStateChanged;
            UnityEditor.EditorApplication.playModeStateChanged += OnPlayModeStateChanged;
            prefabIcon = (Texture2D)UnityEditor.EditorGUIUtility.IconContent("Prefab Icon").image;
        }

        private static void OnPlayModeStateChanged(UnityEditor.PlayModeStateChange state)
        {
            if (state == UnityEditor.PlayModeStateChange.EnteredEditMode)
            {
                goToPath.Clear();
                prefabDict.Clear();
                prefabIcon = null;
                UnityEditor.Editor.finishedDefaultHeaderGUI -= OnDrawHeader;
                UnityEditor.EditorApplication.hierarchyWindowItemOnGUI -= OnInstanceGUI;
                UnityEditor.EditorApplication.playModeStateChanged -= OnPlayModeStateChanged;
            }
        }

        private static void OnInstanceGUI(int instanceId, Rect selectionRect)
        {
            if (!Application.isPlaying) return;
            var go = (GameObject)UnityEditor.EditorUtility.InstanceIDToObject(instanceId);
            if (!go) return;
            if (goToPath.TryGetValue(go, out var path))
            {
                if (prefabIcon != null)
                {
                    Rect iconRect = new Rect(selectionRect.x, selectionRect.y, 16, 16);
                    GUI.DrawTexture(iconRect, prefabIcon);
                }
            }
        }

        [NoToLua]
        public static void OnDrawHeader(UnityEditor.Editor editor)
        {
            if (!Application.isPlaying) return;
            GameObject go = editor.target as GameObject;
            if (!go) return;
            if (goToPath.TryGetValue(go, out var path))
            {
                float originalLabelWidth = UnityEditor.EditorGUIUtility.labelWidth;
                bool originalWordWarp = UnityEditor.EditorStyles.textField.wordWrap;
                UnityEditor.EditorGUIUtility.labelWidth = 32f;
                UnityEditor.EditorStyles.textField.wordWrap = true;

                GUIStyle textStyle = GUI.skin.textField;
                float height = textStyle.CalcHeight(new GUIContent(path), UnityEditor.EditorGUIUtility.currentViewWidth - 55);
                Rect rect = UnityEditor.EditorGUILayout.GetControlRect(GUILayout.Height(height));
                UnityEditor.EditorGUI.TextField(rect, "path:", path);
                if (!prefabDict.TryGetValue(path, out var prefabGO))
                {
                    prefabGO = UnityEditor.AssetDatabase.LoadAssetAtPath<GameObject>(path);
                }
                UnityEditor.EditorGUILayout.ObjectField("go:", prefabGO, typeof(GameObject), true);

                UnityEditor.EditorGUIUtility.labelWidth = originalLabelWidth;
                UnityEditor.EditorStyles.textField.wordWrap = originalWordWarp;
            }
        }
#endif
        
        
        public static GameObject Clone(GameObject sourceGO, GameObject parentGO, string name)
        {
            if (!IsNull(sourceGO))
            {
                var cloneGO = IsNull(parentGO) ? GameObject.Instantiate(sourceGO) : GameObject.Instantiate(sourceGO, parentGO.transform);
                if (!string.IsNullOrEmpty(name))
                {
                    cloneGO.name = name;
                }
#if UNITY_EDITOR
                    if (goToPath.ContainsKey(sourceGO))
                    {
                        goToPath.Add(cloneGO, goToPath[sourceGO]);
                    }
                    else
                    {
                        string path = UnityEditor.PrefabUtility.GetPrefabAssetPathOfNearestInstanceRoot(sourceGO);
                        if (!string.IsNullOrEmpty(path))
                        {
                            goToPath.Add(cloneGO, path);
                        }
                    }
#endif
                return cloneGO;
            }
            Logger.LogError("Clone GameObject error, sourceGO is null!");
            return null;
        }

        //原地克隆（parent为sourceGO的父节点）
        public static GameObject CloneInPlace(GameObject sourceGO, string name)
        {
            if (IsNull(sourceGO))
            {
                Logger.LogError("CloneInPlace error, sourceGO is null!");
                return null;
            }
            var parentTr = sourceGO.transform.parent;
            return Clone(sourceGO, parentTr != null ? parentTr.gameObject : null, name);
        }

        public static void AddChild(this GameObject parentGO, GameObject childGO)
        {
            if (IsNull(childGO))
            {
                Logger.LogError("AddChild GameObject error, childGO = null");
                return;
            }
            childGO.transform.SetParent(parentGO ? parentGO.transform : null, false);
        }

        public static void SetAsFirstSibling(this GameObject go)
        {
            if (!IsNull(go))
            {
                go.transform.SetAsFirstSibling();
            }
        }

        public static void SetAsLastSibling(this GameObject go)
        {
            if (!IsNull(go))
            {
                go.transform.SetAsLastSibling();
            }
        }

        public static void SetSibling(this GameObject go, int index)
        {
            if (!IsNull(go))
            {
                go.transform.SetSiblingIndex(index);
            }
        }

        public static int GetSibling(this GameObject go)
        {
            if (!IsNull(go))
            {
                return go.transform.GetSiblingIndex();
            }
            return -1;
        }

        public static void SetSiblingBefore(this GameObject go, GameObject other)
        {
            if (IsNull(go) || IsNull(other))
            {
                return;
            }
            var goTr = go.transform;
            var otherTr = other.transform;
            var goParentTr = goTr.parent;
            var otherParentTr = otherTr.parent;
            if (goParentTr != otherParentTr)
            {
                goTr.SetParent(otherTr.parent, false);
                go.transform.SetSiblingIndex(otherTr.GetSiblingIndex());
                return;
            }
            int goSibling = goTr.GetSiblingIndex();
            int otherSibling = otherTr.GetSiblingIndex();
            if (goSibling + 1 != otherSibling)
            {
                if (goSibling > otherSibling)
                    goTr.SetSiblingIndex(otherSibling);
                else goTr.SetSiblingIndex(otherSibling - 1);
            }
        }

        public static void SetSiblingAfter(this GameObject go, GameObject other)
        {
            if (IsNull(go) || IsNull(other))
            {
                return;
            }

            var goTr = go.transform;
            var otherTr = other.transform;
            var goParentTr = goTr.parent;
            var otherParentTr = otherTr.parent;

            if (goParentTr != otherParentTr)
            {
                goTr.SetParent(otherTr.parent, false);
                go.transform.SetSiblingIndex(otherTr.GetSiblingIndex() + 1);
                return;
            }

            int goSibling = goTr.GetSiblingIndex();
            int otherSibling = otherTr.GetSiblingIndex();
            if (goSibling != otherSibling + 1)
            {
                goTr.SetSiblingIndex(goSibling > otherSibling ? otherSibling + 1 : otherSibling);
            }
        }

        /// <summary>
        /// 设置物体的层级
        /// </summary>
        /// <param name="go">游戏物体</param>
        /// <param name="layer">层级定义</param>
        /// <param name="recursive">是否递归修改子物体</param>
        public static void SetLayer(this GameObject go, int layer, bool recursive)
        {
            if (!IsNull(go))
            {
                go.layer = layer;
                if (!recursive)
                {
                    return;
                }
                foreach (Transform childTr in go.transform)
                {
                    SetLayer(childTr.gameObject, layer, true);
                }
            }
        }
        /// <summary>
        /// 设置物体的标签
        /// </summary>
        /// <param name="go">游戏物体</param>
        /// <param name="tag">标签定义</param>
        /// <param name="recursive">是否递归修改子物体</param>
        public static void SetTag(this GameObject go, string tag, bool recursive)
        {
            if (!IsNull(go))
            {
                go.tag = tag;
                if (!recursive)
                {
                    return;
                }
                foreach (Transform childTr in go.transform)
                {
                    SetTag(childTr.gameObject, tag, true);
                }
            }
        }

        /// <summary>
        /// 删除go的所有子物体
        /// </summary>
        /// <param name="go">游戏物体</param>
        public static void DestroyAllChildren(this GameObject go)
        {
            if (IsNull(go))
            {
                return;
            }
            Transform rootTr = go.transform;
            int childCount = rootTr.childCount;
            GameObject childGo = null;
            for (int idx = childCount - 1; idx >= 0; idx--)
            {
                childGo = rootTr.GetChild(idx).gameObject;
                GameObject.Destroy(childGo);
            }
            rootTr.DetachChildren();
        }

        /// <summary>
        /// 获取gameobject在hierarchy中的路径
        /// </summary>
        /// <param name="go">gameobject</param>
        /// <returns>路径</returns>
        public static string GetPath(this GameObject go)
        {
            if (IsNull(go))
            {
                return "";
            }
            Transform ptr = go.transform;
            string ret = "";
            while (null != ptr)
            {
                ret = ptr.name + "/" + ret;
                ptr = ptr.parent;
            }
            return ret;
        }
    }
}
